package com.ihr360.shared.basicstaffinfo.service.impl;

import com.ihr360.commons.lang.CollectionUtils;
import com.ihr360.commons.lang.StringUtils;
import com.ihr360.commons.vo.PageData;
import com.ihr360.entity.BasicStaffInfoEntity;
import com.ihr360.request.BasicStaffInfoReq;
import com.ihr360.shared.basicstaffinfo.convertor.SyncedBasicStaffInfoConvertor;
import com.ihr360.shared.basicstaffinfo.dao.SyncedStaffInfoRepository;
import com.ihr360.shared.basicstaffinfo.dto.SyncedBasicStaffInfo;
import com.ihr360.shared.basicstaffinfo.service.BasicStaffInfoService;
import com.ihr360.type.StaffStatus;
import com.ihr360.validation.ValidationUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @author Stone.Shi
 * @description
 * @date 2018-03-07 10:33:29.
 */
@Service
public class BasicStaffInfoServiceImpl implements BasicStaffInfoService {

    @Autowired
    private SyncedStaffInfoRepository reposity;

    @Autowired
    private SyncedBasicStaffInfoConvertor convertor;

    @Override
    public int getInServiceStaffCount(String companyId, List<String> staffIds) {
        if (CollectionUtils.isEmpty(staffIds)) {
            return 0;
        }
        return reposity.countByCompanyIdAndStaffStatusAndIdIn(companyId, StaffStatus.IN_SERVICE, staffIds);
    }

    @Override
    public List<BasicStaffInfoEntity> getStaffList(String companyId, List<String> staffIdList) {
        if (CollectionUtils.isEmpty(staffIdList)) {
            return new ArrayList<>();
        }
        List<SyncedBasicStaffInfo> staffInfoList = reposity.findByCompanyIdAndIdIn(companyId, staffIdList);
        return convertor.convertDtosToEntities(staffInfoList);
    }

    @Override
    public PageData<BasicStaffInfoEntity> getStaffsExclude(String companyId, List<String> staffIds,
                                                           BasicStaffInfoReq req, Pageable pageable) {
        Page<SyncedBasicStaffInfo> pageData = reposity.findAll(getWhereClause(companyId, staffIds, false, req), pageable);
        return new PageData<>(pageData.getTotalPages(), pageData.getTotalElements(),
                convertor.convertDtosToEntities(pageData.getContent()));
    }

    @Override
    public PageData<BasicStaffInfoEntity> getStaffsInclude(String companyId, List<String> staffIds,
                                                           BasicStaffInfoReq req, Pageable pageable) {
        Page<SyncedBasicStaffInfo> pageData = reposity.findAll(getWhereClause(companyId, staffIds, true, req), pageable);
        return new PageData<>(pageData.getTotalPages(), pageData.getTotalElements(),
                convertor.convertDtosToEntities(pageData.getContent()));
    }

    private Specification<SyncedBasicStaffInfo> getWhereClause(String companyId, List<String> staffIds, boolean inIds, BasicStaffInfoReq req) {
        return (root, q, cb) -> {
            Predicate predicate = cb.conjunction();
            predicate.getExpressions().add(cb.equal(root.get(SyncedBasicStaffInfo.Field.companyId).as(String.class), companyId));
            if (req.getStaffStatus() != null) {
                predicate.getExpressions().add(cb.equal(root.get(SyncedBasicStaffInfo.Field.staffStatus).as(String.class),
                        req.getStaffStatus().name()));
            } else {
                predicate.getExpressions().add(cb.equal(root.get(SyncedBasicStaffInfo.Field.staffStatus).as(String.class),
                        StaffStatus.IN_SERVICE.name()));
            }

            if (CollectionUtils.isNotEmpty(staffIds)) {
                Predicate in = root.get(SyncedBasicStaffInfo.Field.id).as(String.class).in(staffIds);
                if (inIds) {
                    predicate.getExpressions().add(in);
                } else {
                    Predicate notIn = cb.not(in);
                    predicate.getExpressions().add(notIn);
                }
            }

            if (StringUtils.isNotBlank(req.getStaffName())) {
                predicate.getExpressions().add(
                        cb.like(root.get(SyncedBasicStaffInfo.Field.staffName).as(String.class), "%" + req.getStaffName() + "%"));
            }

            if (StringUtils.isNotBlank(req.getIdCardNo())) {
                predicate.getExpressions().add(
                        cb.like(root.get(SyncedBasicStaffInfo.Field.idCardNo).as(String.class), req.getIdCardNo() + "%"));
            }

            if (CollectionUtils.isNotEmpty(req.getDepartmentIds())) {
                predicate.getExpressions().add(
                        root.get(SyncedBasicStaffInfo.Field.departmentId).as(Long.class).in(req.getDepartmentIds()));
            }

            return predicate;
        };
    }

    @Override
    public Page<BasicStaffInfoEntity> getStaffsInclude(String companyId, List<String> staffIds,
                                                       Specification<SyncedBasicStaffInfo> specification,
                                                       Pageable pageable) {
        if (CollectionUtils.isEmpty(staffIds)) {
            return new PageImpl<>(Collections.emptyList());
        }
        Specification<SyncedBasicStaffInfo> whereClause = getWhereClause(companyId, true, staffIds, specification);
        Page<SyncedBasicStaffInfo> page = reposity.findAll(whereClause, pageable);
        return new PageImpl<>(convertor.convertDtosToEntities(page.getContent()), pageable, page.getTotalElements());
    }

    @Override
    public Page<BasicStaffInfoEntity> getStaffsExclude(String companyId, List<String> staffIds,
                                                       Specification<SyncedBasicStaffInfo> specification,
                                                       Pageable pageable) {
        Specification<SyncedBasicStaffInfo> whereClause = getWhereClause(companyId, false, staffIds, specification);
        Page<SyncedBasicStaffInfo> page = reposity.findAll(whereClause, pageable);
        return new PageImpl<>(convertor.convertDtosToEntities(page.getContent()), pageable, page.getTotalElements());
    }

    private Specification<SyncedBasicStaffInfo> getWhereClause(String companyId, boolean isInStaffIds,
                                                               List<String> staffIds, Specification<SyncedBasicStaffInfo> specification) {
        return (root, cq, cb) -> {
            Predicate predicate = cb.conjunction();
            List<Expression<Boolean>> expressionList = predicate.getExpressions();
            expressionList.add(cb.equal(root.get(SyncedBasicStaffInfo.Field.companyId).as(String.class), companyId));
            if (isInStaffIds) {
                //查询id在指定范围内的数据
                ValidationUtils.rejectIfEmpty(staffIds, null, "给定的staffIds参数不能为空");
                expressionList.add(root.get(SyncedBasicStaffInfo.Field.id).as(String.class).in(staffIds));
            } else {
                //查询id在指定范围外的数据
                if (CollectionUtils.isNotEmpty(staffIds)) {
                    expressionList.add(cb.not(root.get(SyncedBasicStaffInfo.Field.id).as(String.class).in(staffIds)));
                }
            }
            if (specification != null) {
                Predicate searchPredicate = specification.toPredicate(root, cq, cb);
                if(searchPredicate != null) {
                    return cb.and(predicate, searchPredicate);
                }
            }
            return predicate;
        };
    }

    @Override
    public List<BasicStaffInfoEntity> getStaffsIncludeNoPage(String companyId, Specification<SyncedBasicStaffInfo> specification) {
        List<SyncedBasicStaffInfo> list = reposity.findAll(getWhereClause(companyId, false, null, specification));
        return convertor.convertDtosToEntities(list);
    }
}
